package nio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;

public class SelectorThread implements Runnable {

    private Selector selector;

    private SelectorThreadGroup group;

    private LinkedBlockingQueue<Channel> channelQueue;

    public SelectorThread(SelectorThreadGroup group) {
        try {
            selector = Selector.open();
            channelQueue = new LinkedBlockingQueue<>();
            this.group = group;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                // 1. select
                System.out.println(Thread.currentThread().getName()
                        + " before select " + selector.keys().size());
                int n = selector.select();
                System.out.println(Thread.currentThread().getName()
                        + " after select " + selector.keys().size());
                // 2. 处理keys
                if (n > 0) {
                    Set<SelectionKey> keys = selector.selectedKeys();
                    System.out.println("selectedKeys.size:" + keys.size());
//                    keys.forEach(key -> {
//                        if (key.isAcceptable()) {
//                            handAccept(key);
//                        } else if (key.isReadable()) {
//                            handRead(key);
//                        } else if (key.isWritable()) {
//                            handWrite(key);
//                        }
//                    });
                    Iterator<SelectionKey> iterator = keys.iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        iterator.remove();
                        if (key.isAcceptable()) {
                            handAccept(key);
                        } else if (key.isReadable()) {
                            handRead(key);
                        } else if (key.isWritable()) {
                            handWrite(key);
                        }
                    }
                }
                // 3. do tasks
                while (!channelQueue.isEmpty()) {
                    System.out.println("channelQueue.size:" + channelQueue.size());
                    Channel channel = channelQueue.take();
                    if (channel instanceof ServerSocketChannel) {
                        ((ServerSocketChannel) channel).register(selector, SelectionKey.OP_ACCEPT);
                        System.out.println(Thread.currentThread().getName() + " a server register ");
                    } else  if (channel instanceof SocketChannel) {
                        ((SocketChannel) channel).register(selector,
                                SelectionKey.OP_READ,
                                ByteBuffer.allocate(4096));
                        System.out.println(Thread.currentThread().getName() + " a client register ");
                    }
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void handWrite(SelectionKey key) {
        System.out.println("handWrite...");
    }

    private void handRead(SelectionKey key) {
        System.out.println("handRead...");
        SocketChannel client = (SocketChannel) key.channel();
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        try {
            while (true) {
                int read = client.read(buffer);
                if (read > 0) {
                    buffer.flip();
                    while (buffer.hasRemaining()) {
                        client.write(buffer);
                    }
                    buffer.clear();
                } else if (read == 0) {
                    break;
                } else {
                    System.out.println("客户端断开了");
                    key.cancel();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void handAccept(SelectionKey key) {
        System.out.println("handAccept...");
        ServerSocketChannel channel = (ServerSocketChannel) key.channel();
        try {
            SocketChannel client = channel.accept();
            client.configureBlocking(false);
            group.regToNextSelector(client);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Selector getSelector() {
        return selector;
    }

    public LinkedBlockingQueue<Channel> getChannelQueue() {
        return channelQueue;
    }
}
